package com.lry.rpc.netty.client;

import com.lry.common.CommonConstants;
import com.lry.event.ProviderNodeWrapper;
import com.lry.exception.FrameworkException;
import com.lry.protocol.RpcInvocation;
import com.lry.protocol.RpcMessage;
import com.lry.proxy.Invoker;
import com.lry.proxy.RpcInvoker;
import com.lry.rpc.config.RpcReferenceBean;
import com.lry.rpc.config.RpcServiceBean;
import com.lry.rpc.netty.BeanKey;
import com.lry.rpc.netty.server.ServerCache;
import com.lry.util.CollectionUtils;
import com.lry.util.PositiveAtomicCounter;
import io.netty.channel.Channel;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import static com.lry.common.CommonConstants.COLON;

public class ClientCache {

    public final static PositiveAtomicCounter ID_GEN = new PositiveAtomicCounter();

    //<requestId,响应的结果报文>
    private static Map<Integer, RpcMessage> PENDING = new ConcurrentHashMap<>();

    //<BeanKey,<group,List<Invoker>>>
    private static Map<BeanKey,Map<String,List<Invoker>>> INVOKER_MAP = new HashMap<>();

    //<BeanKey,<group,RpcReferenceBean>>
    private static Map<BeanKey, Map<String,RpcReferenceBean>> REFERENCE_BEAN_MAP = new HashMap<>();


    public static void putServiceNameAndChannel(String implName, Channel channel, ProviderNodeWrapper providerNodeWrapper){

        BeanKey beanKey = new BeanKey(providerNodeWrapper.getServiceName(),implName,providerNodeWrapper.getVersion());

        INVOKER_MAP.compute(beanKey,(k, v)->{
            if(null==v){
                v = new HashMap<>();
            }

            v.compute(providerNodeWrapper.getGroup(),(k1,v1)->{
                if(null==v1) {
                    v1 = new ArrayList<>();
                }
                ChannelWrapper channelWrapper = ChannelWrapper.builder()
                        .channel(channel)
                        .ip(providerNodeWrapper.getHost())
                        .port(providerNodeWrapper.getPort())
                        .weight(providerNodeWrapper.getWeight())
                        .build();

                Invoker rpcInvoker = new RpcInvoker(channelWrapper);
                v1.add(rpcInvoker);
                return v1;
            });

            return v;
        });
    }

    public static List<Invoker> getInvokers(RpcInvocation rpcInvocation){
        String group = (String) rpcInvocation.getAttachments().get(CommonConstants.SPEC_GROUP_KEY);
        BeanKey beanKey = BeanKey.getBeanKey(rpcInvocation);
        return getInvokers(beanKey,group);
    }

    public static List<Invoker> getInvokers(BeanKey beanKey,String group){
        Map<String, List<Invoker>> group2Invoker = INVOKER_MAP.get(beanKey);
        if(CollectionUtils.isEmpty(group2Invoker)){
            throw new FrameworkException(String.format("group2Invoker is null,beanKey=%s",beanKey));
        }
        if(null==group){
            group = "";
        }
        return group2Invoker.get(group);
    }

    public static Invoker getInvoker(BeanKey beanKey,String group,String ips){
        String[] split = ips.split(COLON);
        String ip = split[0];
        Integer port = Integer.valueOf(split[1]);
        List<Invoker> invokers = getInvokers(beanKey,group);

        for (Invoker invoker : invokers) {
            boolean ans = invoker.ipPortEquals(ip,port);
            if(ans){
                return invoker;
            }
        }
        return null;
    }


    public static Set<BeanKey> beanKeySet(){
        return REFERENCE_BEAN_MAP.keySet();
    }

    public static boolean isReferenceBeanEmpty(){
        return REFERENCE_BEAN_MAP.isEmpty();
    }

    public static void putReferenceBean(RpcReferenceBean rpcReferenceBean){
        if(null==rpcReferenceBean){
            throw new FrameworkException("rpcReferenceBean is null");
        }

        String interfaceName = rpcReferenceBean.getInterfaceClass().getName();
        String implName = rpcReferenceBean.getServiceImplName();
        String version = rpcReferenceBean.getVersion();
        BeanKey beanKey = new BeanKey(interfaceName,implName,version);

        REFERENCE_BEAN_MAP.compute(beanKey,(k,v)->{
            if(null==v){
                v = new HashMap<>();
            }
            v.put(rpcReferenceBean.getGroup(),rpcReferenceBean);
            return v;
        });
    }

    public static RpcReferenceBean getReferenceBean(RpcInvocation rpcInvocation){

        String group = (String) rpcInvocation.getAttachments().get(CommonConstants.SPEC_GROUP_KEY);
        BeanKey beanKey = BeanKey.getBeanKey(rpcInvocation);
        RpcReferenceBean rpcReferenceBean = ClientCache.getReferenceBean(beanKey,group);
        if(null==rpcReferenceBean){
            throw new FrameworkException("rpcReferenceBean is null");
        }
        return rpcReferenceBean;
    }


    public static RpcReferenceBean getReferenceBean(BeanKey beanKey, String group){
        Map<String, RpcReferenceBean> group2RpcReferenceBean = REFERENCE_BEAN_MAP.get(beanKey);
        if(CollectionUtils.isEmpty(group2RpcReferenceBean)){
            throw new FrameworkException(String.format("RpcReferenceBean is null,beanKey=%s",beanKey));
        }

        if(null==group){
            group = "";
        }
        RpcReferenceBean rpcReferenceBean = group2RpcReferenceBean.get(group);
        if(null==rpcReferenceBean){
            throw new FrameworkException(String.format("找不到服务,beanKey=%s",beanKey));
        }
        return rpcReferenceBean;
    }




    public static RpcMessage removeRpcMessageByRequestId(int requestId){
        return PENDING.remove(requestId);
    }


    public static void putToPending(int requestId,RpcMessage response){
        PENDING.put(requestId,response);
    }

    public static void removeReferenceBean(RpcReferenceBean rpcReferenceBean) {

    }
}
